package com.alien.admin.modules.basic.service.impl;

import com.alien.admin.exception.BadRequestException;
import com.alien.admin.modules.deploy.domain.DpK8sInstance;
import com.alien.admin.modules.basic.domain.Deployment;
import com.alien.admin.modules.basic.domain.Pod;
import com.alien.admin.modules.basic.domain.PodMetric;
import com.alien.admin.modules.basic.domain.ReplicaSet;
import com.alien.admin.modules.basic.service.K8sService;
import com.alien.admin.utils.DateUtil;
import com.alien.admin.utils.JsonUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class K8sServiceImpl implements K8sService {
    final RestTemplate restTemplate = new RestTemplate();

    @Value("${deployServer.postUrl}")
    String postUrl = "";

    private HttpEntity<String> buildPostEntity(K8sPostEntity postEntity) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        String tokenPostVOStr = JsonUtil.object2Json(postEntity);
        HttpEntity<String> request = new HttpEntity<String>(tokenPostVOStr, httpHeaders);
        return request;
    }

    private BaseResponseEntity post(K8sPostEntity postEntity, String methodUrl) {
        HttpEntity<String> request = buildPostEntity(postEntity);
        String getJobUrl = postUrl + "/k8s/" + methodUrl;
        BaseResponseEntity jenkinsResponseEntity = restTemplate.postForObject(getJobUrl, request, BaseResponseEntity.class);
        String status = jenkinsResponseEntity.getStatus();
        if("error".equals(status)) {
            throw new BadRequestException(jenkinsResponseEntity.getError());
        }
        return jenkinsResponseEntity;
    }

    @Override
    public void deploy(DpK8sInstance instance, String deploymentYmlText) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);

        String encodeDeploymentYmlText = "";
        try {
            encodeDeploymentYmlText = Base64.getEncoder().encodeToString(deploymentYmlText.getBytes("utf-8"));
        } catch (Exception e) {
            throw new BadRequestException("编码失败");
        }
        postEntity.setDeploymentYmlText(encodeDeploymentYmlText);
        post(postEntity, "deploy");
    }

    @Override
    public boolean healthCheck(DpK8sInstance instance, String namespace, String podName) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        postEntity.setPodName(podName.toLowerCase());
        BaseResponseEntity baseResponseEntity = post(postEntity, "healthCheck");
        Object data = baseResponseEntity.getData();
        return (Boolean)data;
    }

    @Override
    public String getDeploymentLog(DpK8sInstance instance, String namespace, String podName) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        postEntity.setPodName(podName.toLowerCase());
        BaseResponseEntity baseResponseEntity = post(postEntity, "getDeploymentLog");
        Object data = baseResponseEntity.getData();
        return (String)data;
    }

    private Deployment parseDeploymentObj(Object deploymentObj) {
        Map<String, Object> dataMap = (Map<String, Object>)deploymentObj;
        Deployment deployment = new Deployment();
        deployment.setName((String)dataMap.get("Name"));

        Map<String, Object> statusMap = (Map<String, Object>)dataMap.get("Status");
        Deployment.DeploymentStatus deploymentStatus = new Deployment.DeploymentStatus();

        deploymentStatus.setReplicas((Integer)statusMap.get("replicas"));
        deploymentStatus.setUpdatedReplicas((Integer)statusMap.get("updatedReplicas"));
        deploymentStatus.setReadyReplicas((Integer)statusMap.get("readyReplicas"));
        deploymentStatus.setAvailableReplicas((Integer)statusMap.get("availableReplicas"));

        List<Map<String, Object>> conditions = (List<Map<String, Object>>)statusMap.get("conditions");
        List<Deployment.DeploymentStatusCondition> deploymentStatusConditionList =
                conditions.stream().map(condition -> {
                    String type = (String)condition.get("type");
                    String status = (String)condition.get("status");
                    String lastUpdateTime = (String)condition.get("lastUpdateTime");
                    String lastTransitionTime = (String)condition.get("lastTransitionTime");
                    String reason = (String)condition.get("reason");
                    String message = (String)condition.get("message");

                    Deployment.DeploymentStatusCondition dc = new Deployment.DeploymentStatusCondition();
                    dc.setType(type);
                    dc.setStatus(Boolean.valueOf(status));
                    dc.setLastUpdateTime(lastUpdateTime);
                    dc.setLastTransitionTime(lastTransitionTime);
                    dc.setReason(reason);
                    dc.setMessage(message);

                    return dc;
                }).collect(Collectors.toList());

        deploymentStatus.setConditions(deploymentStatusConditionList);
        deployment.setStatus(deploymentStatus);

        // 获取resource
        Map<String, Object> specMap = (Map<String, Object>)dataMap.get("Spec");
        Map<String, Object> templateMap = (Map<String, Object>)specMap.get("template");
        if(templateMap != null) {
            Map<String, Object> littleSpecMap = (Map<String, Object>)templateMap.get("spec");
            if(littleSpecMap != null) {
                List<Object> containersObj = (List<Object>)littleSpecMap.get("containers");
                if(containersObj != null && !containersObj.isEmpty()) {
                    Map<String, Object> firstContainerObj = (Map<String, Object>)containersObj.get(0);
                    Map<String, Object> resources = (Map<String, Object>)firstContainerObj.get("resources");
                    if(resources != null) {
                        Map<String, Object> limits = (Map<String, Object>)resources.get("limits");
                        Map<String, Object> requests = (Map<String, Object>)resources.get("requests");

                        String limitCpu = String.valueOf(limits.get("cpu"));
                        String limitMem = String.valueOf(limits.get("memory"));
                        String requestCpu = String.valueOf(requests.get("cpu"));
                        String requestMem = String.valueOf(requests.get("memory"));

                        Deployment.DeploymentResource deploymentResource =  new Deployment.DeploymentResource();
                        deploymentResource.setLimitCpu(limitCpu);
                        deploymentResource.setLimitMem(limitMem);
                        deploymentResource.setRequestCpu(requestCpu);
                        deploymentResource.setRequestMem(requestMem);

                        deployment.setDeploymentResource(deploymentResource);
                    }
                }
            }
        }

        return deployment;
    }

    @Override
    public List<Deployment> getDeployments(DpK8sInstance instance, String namespace) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        BaseResponseEntity baseResponseEntity = post(postEntity, "getDeployments");
        Object data = baseResponseEntity.getData();
        List<Object> deploymentObjList = (List<Object>)data;
        if(deploymentObjList == null) {
            return new ArrayList<>(0);
        }

        List<Deployment> deploymentList = deploymentObjList.stream().map(item -> parseDeploymentObj(item))
                .collect(Collectors.toList());

        return deploymentList;
    }

    PodMetric parsePodMetric(Object podMetricObj) {
        Map<String, Object> dataMap = (Map<String, Object>)podMetricObj;
        Long mem = Long.valueOf(String.valueOf(dataMap.get("Mem")));
        Double cpu =  Double.valueOf(String.valueOf(dataMap.get("Cpu")));

        PodMetric podMetric = new PodMetric();
        podMetric.setPodName(String.valueOf(dataMap.get("PodName")));
        podMetric.setCpu(cpu);
        podMetric.setMem(mem);

        return podMetric;
    }

    @Override
    public List<PodMetric> getPodMetrics(DpK8sInstance instance, String namespace) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        BaseResponseEntity baseResponseEntity = post(postEntity, "getPodMetrics");
        Object data = baseResponseEntity.getData();
        List<Object> podMetricObjs = (List<Object>)data;
        if(podMetricObjs == null) {
            return new ArrayList<>(0);
        }

        List<PodMetric> podMetricList = podMetricObjs.stream().map(item -> parsePodMetric(item))
                .collect(Collectors.toList());

        return podMetricList;
    }

    ReplicaSet parseReplicaSet(Object replicaSetObj) {
        Map<String, Object> dataMap = (Map<String, Object>)replicaSetObj;
        Map<String, Object> metaDataMap = (Map<String, Object>)dataMap.get("metadata");

        String name = String.valueOf(metaDataMap.get("name"));
        String creationTimestamp = String.valueOf(metaDataMap.get("creationTimestamp"));

        Map<String, Object> annotationsMap = (Map<String, Object>)metaDataMap.get("annotations");
        Integer revision = Integer.valueOf(String.valueOf(annotationsMap.get("deployment.kubernetes.io/revision")));


        Map<String, Object> statusMap = (Map<String, Object>)dataMap.get("status");
        Integer replicas = Integer.valueOf(String.valueOf(statusMap.get("replicas")));

        Integer readyReplicas = Integer.valueOf(String.valueOf(statusMap.getOrDefault("readyReplicas", 0)));
        Integer availableReplicas =Integer.valueOf(String.valueOf(statusMap.getOrDefault("availableReplicas", 0)));

        String status = "";
        if(replicas.equals(0)) {
            status = "completed";
        } else if(availableReplicas < replicas) {
            status = "warning";
        } else {
            status = "success";
        }

        ReplicaSet replicaSet = new ReplicaSet();
        replicaSet.setName(name);
        replicaSet.setReplicas(replicas);
        replicaSet.setReadyReplicas(readyReplicas);
        replicaSet.setAvailableReplicas(availableReplicas);
        replicaSet.setRevision(revision);
        replicaSet.setStatus(status);

        replicaSet.setCreationTimestampDate(DateUtil.parse8sDate(creationTimestamp));
        replicaSet.setCreationTimestamp(DateUtil.parse8sDateStr(creationTimestamp));

        return replicaSet;
    }

    @Override
    public List<ReplicaSet> getReplicaSets(DpK8sInstance instance, String namespace, String deploymentName) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        postEntity.setDeploymentName(deploymentName.toLowerCase());
        BaseResponseEntity baseResponseEntity = post(postEntity, "getReplicaSet");
        Object data = baseResponseEntity.getData();
        List<Object> replicaList = (List<Object>)data;
        if(replicaList == null) {
            return new ArrayList<>(0);
        }

        List<ReplicaSet> replicaSetList = replicaList.stream().map(item -> parseReplicaSet(item))
                .collect(Collectors.toList());

        // 时间降序
        Collections.sort(replicaSetList, (r1, r2) -> r2.getCreationTimestampDate().compareTo(r1.getCreationTimestampDate()));
        return replicaSetList;
    }

    Pod parsePodObj(Object podObj) {
        Map<String, Object> dataMap = (Map<String, Object>) podObj;
        Map<String, Object> metaDataMap = (Map<String, Object>)dataMap.get("metadata");

        String podName = String.valueOf(metaDataMap.get("name"));
        String creationTimestamp = String.valueOf(metaDataMap.get("creationTimestamp"));

        Map<String, Object> statusMap = (Map<String, Object>)dataMap.get("status");

        String phase = String.valueOf(statusMap.get("phase"));
        String hostIP = String.valueOf(statusMap.get("hostIP"));
        String podIP = String.valueOf(statusMap.get("podIP"));

        Pod pod = new Pod();
        List<Object> containerStatusesObjList = (List<Object>)statusMap.get("containerStatuses");
        if(containerStatusesObjList != null) {
            Map<String, Object> containerStatusesMap = (Map<String, Object>)containerStatusesObjList.get(0);
            Boolean ready = Boolean.valueOf(String.valueOf(containerStatusesMap.get("ready")));
            Integer restartCount = Integer.valueOf(String.valueOf(containerStatusesMap.get("restartCount")));

            Map<String, Object> containerStatusStatesMap = (Map<String, Object>)containerStatusesMap.get("state");
            Object[] containerStatusStatesObj = containerStatusStatesMap.keySet().toArray();
            if(containerStatusStatesObj.length > 0) {
                String state = String.valueOf(containerStatusStatesObj[0]);
                pod.setContainerStatusesState(state);
            }

            pod.setPodIP(podIP);
            pod.setReady(ready);
            pod.setRestartCount(restartCount);
        }

        // 获取端口
        Map<String, Object> specMap = (Map<String, Object>)dataMap.get("spec");
        List<Object> containers = (List<Object>)specMap.get("containers");
        if(containers != null && !containers.isEmpty()) {
            Map<String, Object> container = (Map<String, Object>)containers.get(0);
            List<Object> portsObj = (List<Object>)container.get("ports");
            String image = String.valueOf(container.get("image"));
            String containerName = String.valueOf(container.get("name"));

            pod.setPorts(getPortList(portsObj));
            pod.setImage(image);
            pod.setContainerName(containerName);
        }
        pod.setPodName(podName);
        pod.setPhase(phase);
        pod.setHostIP(hostIP);

        pod.setCreationTimestampDate(DateUtil.parse8sDate(creationTimestamp));
        pod.setCreationTimestamp(DateUtil.parse8sDateStr(creationTimestamp));

        List<Object> conditionsObject = (List<Object>)statusMap.get("conditions");
        parsePodConditions(pod, conditionsObject);

        return pod;
    }

    List<Pod.Port> getPortList(List<Object> portsObj) {
        List<Pod.Port> portList = portsObj.stream().map(portObj -> {
            Map<String, Object> portMap = (Map<String, Object>)portObj;
            String name = String.valueOf(portMap.get("name"));
            String protocol = String.valueOf(portMap.get("protocol"));
            Integer containerPort = Integer.valueOf(String.valueOf(portMap.get("containerPort")));

            Pod.Port port = new Pod.Port();
            port.setName(name);
            port.setProtocol(protocol);
            port.setContainerPort(containerPort);

            return port;
        }).collect(Collectors.toList());

        return portList;
    }

    void parsePodConditions(Pod pod, List<Object> conditionObjList) {
        conditionObjList.stream().forEach(conditionObj -> {
            Map<String, Object> condition = (Map<String, Object>)conditionObj;
            String type = String.valueOf(condition.get("type"));
            Pod.Condition c = parsePodCondition(condition);
            if("Initialized".equals(type)) {
                pod.setInitialized(c);
            } else if("Ready".equals(type)) {
                pod.setReadyCondition(c);
            } else if("ContainersReady".equals(type)) {
                pod.setContainersReady(c);
            } else if("PodScheduled".equals(type)) {
                pod.setPodScheduled(c);
            }
        });
    }

    Pod.Condition parsePodCondition(Map<String, Object> conditionMap) {
        Pod.Condition pc = new Pod.Condition();
        pc.setReason(String.valueOf(conditionMap.getOrDefault("reason", "")));
        pc.setMessage(String.valueOf(conditionMap.getOrDefault("message", "")));
        pc.setLastTransitionTime(DateUtil.parse8sDateStr(String.valueOf(conditionMap.get("lastTransitionTime"))));
        pc.setLastProbeTime(String.valueOf(conditionMap.get("lastProbeTime")));
        pc.setStatus(Boolean.valueOf(String.valueOf(conditionMap.get("status")).toLowerCase()));
        return pc;
    }

    @Override
    public List<Pod> getPods(DpK8sInstance instance, String namespace, String replicaSetName) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        postEntity.setReplicaSetName(replicaSetName.toLowerCase());
        BaseResponseEntity baseResponseEntity = post(postEntity, "getPods");
        Object data = baseResponseEntity.getData();
        List<Object> podObjList = (List<Object>)data;
        if(podObjList == null) {
            return new ArrayList<>(0);
        }

        List<Pod> podList = podObjList.stream().map(item -> parsePodObj(item))
                .collect(Collectors.toList());

        // 时间降序
        Collections.sort(podList, (r1, r2) -> r2.getCreationTimestampDate().compareTo(r1.getCreationTimestampDate()));
        return podList;
    }

    @Override
    public String getPodLog(DpK8sInstance instance, String namespace, String podName) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        postEntity.setPodName(podName.toLowerCase());
        BaseResponseEntity baseResponseEntity = post(postEntity, "getPodLog");
        Object data = baseResponseEntity.getData();
        return (String)data;
    }

    @Override
    public void rollback(DpK8sInstance instance, String namespace, String deploymentName, String rollbackReplicaSetName) {
        K8sPostEntity postEntity = new K8sPostEntity(instance);
        postEntity.setNamespace(namespace);
        postEntity.setDeploymentName(deploymentName.toLowerCase());
        postEntity.setRollbackReplicaSetName(rollbackReplicaSetName);
        post(postEntity, "rollback");
    }

    @Data
    private static class K8sPostEntity {

        public K8sPostEntity(DpK8sInstance instance) {
            // 进行base64编码
            String encodeCfgContent = "";
            try {
                encodeCfgContent = Base64.getEncoder().encodeToString(instance.getContent().getBytes("utf-8"));
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.configFileStr = encodeCfgContent;
        }
        private String configFileStr;
        private String namespace;
        private String podName;
        private String replicaSetName;
        private String deploymentName;
        private String deploymentYmlText;
        private String rollbackReplicaSetName;
    }
}
